local Tween = class("Tween")
function Tween:ctor(obj, tp, prms, tgts,target,yoyo)
    self.t   = 0       -- current time of tween (0 .. dur)
    self.obj = obj     -- object
    self.tp  = tp      -- tweening parameters
    self.target = target --方法返回的对象

    self.prms = prms   -- parameter (string)
    self.tgts = tgts
    self.bgns = {} -- starting value
    self.cngs = {} -- change (total during the whole tween)
    
    self.yoyo = yoyo or false --是否来回播放
end







--[[
function UITestScene:createTweener(tweenObj)
    local Tweener = require("ext.Tweener")

    local o = {x=tweenObj:getUIPositionX(),y=tweenObj:getUIPositionY()}

    --因为参数中加了target，所以第一个参数，就是target，参数在第二个参数param中
    local function onStart(target,param)
        print("onStart")
    end
    local function onUpdate()
        tweenObj:setUIPosition(o.x,o.y)
    end 

    Tweener.addTween(o, {
        x=400, y=500,
        target = self, --这里设置了target ,所以onTweenComplete可以接收到params
        time=0.5, delay=1, transition="easeInOutBounce",
        onStart=onStart,
        onStartParams = {start=1},
        onUpdate = onUpdate,
        onComplete=self.onTweenComplete,
        onCompleteParams={test="tt"}
    })
end

function UITestScene:onTweenComplete(params)
    print("onComplete - param:"..params.test)
end
--]]
----@module ext.Tweener 缓动类
local Tweener = {

        twns={},
        looping = false,
        _ptime = 0,
        def={
            time= 1,
            transition= "easeOutExpo",
            delay= 0,
            onStart= {},
            onStartParams= {},
            onUpdate= {},
            onUpdateParams= {},
            onComplete= {},
            onCompleteParams= {}
        }

}
Tweener.EaseNone="easeNone"
Tweener.EaseInQuad ="easeInQuad"
Tweener.EaseOutQuad="easeOutQuad"
Tweener.EaseInOutQuad ="easeInOutQuad"
Tweener.EaseInCubic="easeInCubic"
Tweener.EaseOutCubic="easeOutCubic"
Tweener.EaseInOutCubic  ="easeInOutCubic"
Tweener.EaseOutInCubic ="easeOutInCubic"
Tweener.EaseInQuart="easeInQuart"
Tweener.EaseOutQuart="easeOutQuart"
Tweener.EaseInOutQuart  ="easeInOutQuart"
Tweener.EaseOutInQuart="easeOutInQuart"
Tweener.EaseInQuint="easeInQuint"
Tweener.EaseOutQuint="easeOutQuint"
Tweener.EaseInOutQuint="easeInOutQuint"
Tweener.EaseOutInQuint="easeOutInQuint"
Tweener.EaseInSine="easeInSine"
Tweener.EaseOutSine="easeOutSine"
Tweener.EaseInOutSine="easeInOutSine"
Tweener.EaseOutInSine="easeOutInSine"
Tweener.EaseInExpo="easeInExpo"
Tweener.EaseOutExpo="easeOutExpo"
Tweener.EaseInOutExpo  ="easeInOutExpo"
Tweener.EaseOutInExpo ="easeOutInExpo"
Tweener.EaseInCirc ="easeInCirc"
Tweener.EaseOutCirc="easeOutCirc"
Tweener.EaseInOutCirc   ="easeInOutCirc"
Tweener.EaseOutInCirc="easeOutInCirc"
Tweener.EaseInElastic ="easeInElastic"
Tweener.EaseOutElastic   ="easeOutElastic"
Tweener.EaseInOutElastic ="easeInOutElastic"
Tweener.EaseOutInElastic="easeOutInElastic"
Tweener.EaseInBack="easeInBack"
Tweener.EaseOutBack ="easeOutBack"
Tweener.EaseInOutBack   ="easeInOutBack"
Tweener.EaseOutInBack ="easeOutInBack"
Tweener.EaseInBounce="easeInBounce"
Tweener.EaseOutBounce  ="easeOutBounce"
Tweener.EaseInOutBounce   ="easeInOutBounce"
Tweener.EaseOutInBounce     ="easeOutInBounce"


----@function [parent=#ext.Tweener] addTween
----@param #table o 一个key-value的table , 其中value是number类型
----@param #table ps 各个参数。其中如果设置了target ,那回调方法，最好是类方法。如果不加target，则回调方法应该是私有方法
Tweener.addTween=function(o,ps)
    local tp , prms, tgts = {},{},{}
    
    for k, p in pairs(Tweener.def) do
        tp[k] = ps[k] and ps[k] or nil
    end
    for k, p in pairs(ps) do
    	if not Tweener.def.k then
            table.insert(prms,k)
            table.insert(tgts,p)
        end
    end
    
    if tp.onStart then
        if tp.onStartParams  then
            if ps.target then
                tp.onStart(ps.target,tp.onStartParams)  
            else
                tp.onStart(tp.onStartParams) 
            end
        else
            if ps.target then
                tp.onStart(ps.target)
            else
                tp.onStart()
            end
        end
    end
    
    table.insert(Tweener.twns,Tween.new(o, tp, prms, tgts,ps.target,ps.yoyo))
    Tweener.looping = true
    Tweener._requestUpdate()
end


----停止一个Tween
----@function [parent=#ext.Tweener] kill
----@param #table o 停止Tween
Tweener.kill = function(o)
    for i, var in ipairs(Tweener.twns) do
    	if o==var.obj then
            table.remove(Tweener.twns,i)
            if table.getn(Tweener.twns)>0 then 
                Tweener.looping = true
                Tweener._requestUpdate()
            else 
                Tweener.looping = false
                if Tweener._entryID then
                    cc.Director:getInstance():getScheduler():unscheduleScriptEntry(Tweener._entryID)
                end
            end
            break
    	end
    end
end

Tweener._requestUpdate = function()
    if Tweener._entryID then
        cc.Director:getInstance():getScheduler():unscheduleScriptEntry(Tweener._entryID)
    end
    local schdu = cc.Director:getInstance():getScheduler()
    Tweener._entryID = schdu:scheduleScriptFunc(Tweener._step,0.001,false)
end

Tweener._step = function(dt)
    for i, t in ipairs(Tweener.twns) do
    	if t.tp.delay>0 then
    	   t.tp.delay = t.tp.delay-dt
    	else
    	   if #t.bgns==0 then
    	       for j, var in ipairs(t.prms) do
                    if t.obj[var] then
                        table.insert(t.bgns,t.obj[t.prms[j]])
                        table.insert(t.cngs,t.tgts[j]-t.bgns[#t.bgns])
                   end
    	       end
    	   end
    	   t.t = t.t + dt
    	   local dur = t.tp.time
    	   for j, var in ipairs(t.prms) do
    	       if j<=#t.bgns and j<=#t.cngs then
        	   	   if t.t > dur  then 
        	   	       t.obj[var] = t.bgns[j]+t.cngs[j]
                   else
                       t.obj[var] = Tweener._easingFunctions[t.tp.transition] (t.t, t.bgns[j], t.cngs[j], dur)
                   end
               end
    	   end
    	   if t.tp.onUpdate then
    	       if t.tp.onUpdateParams  then
    	           if t.target then
                       t.tp.onUpdate( t.target,t.tp.onUpdateParams)
    	           else
    	               t.tp.onUpdate( t.tp.onUpdateParams)
    	           end
    	       else 
    	           if t.target then
                        t.tp.onUpdate(t.target)
    	           else
                        t.tp.onUpdate()
    	           end
    	       end
    	   end
            if t.t > dur then
                if t.yoyo==true then
                    t.t = 0
                    for i, var in ipairs(t.bgns) do
                        t.tgts[i] ,t.bgns[i] = t.bgns[i],  t.tgts[i]
                        t.cngs[i] = -t.cngs[i]
                    end
                else       
                    table.remove(Tweener.twns,i)
                end
                if t.tp.onComplete then
                   if(t.tp.onCompleteParams) then
                        if t.target then
                            t.tp.onComplete(t.target, t.tp.onCompleteParams)
                        else
                            t.tp.onComplete( t.tp.onCompleteParams)
                        end
                   else 
                        if t.target then
                            t.tp.onComplete(t.target)
                        else
                            t.tp.onComplete()
                        end
                        
                   end
                end
            end
    	end
    end
    
    if table.getn(Tweener.twns)>0 then 
        Tweener.looping = true
        Tweener._requestUpdate()
    else 
        Tweener.looping = false
        if Tweener._entryID then
            cc.Director:getInstance():getScheduler():unscheduleScriptEntry(Tweener._entryID)
        end
    end
end


Tweener._easingFunctions = 
{
    --[[
        t - current time of tween
        b - starting value of property
        c - change needed in value
        d - total duration of tween
    --]]
    easeNone= function(t, b, c, d)
        return c*t/d + b
    end, 
    easeInQuad=function(t, b, c, d)
        local t = t/d
        return c*t*t + b
    end,    
    easeOutQuad=function(t, b, c, d)
        local t = t/d
        return -c *t*(t-2) + b
    end,    
    easeInOutQuad= function(t, b, c, d)
        t = t/d*2
        if t< 1 then return c/2*t*t + b end
        
        t = t-1
        return -c/2 *(t*(t-2) - 1) + b;
    end,    
    easeInCubic= function(t, b, c, d)
        local t = t/d
        return c*t*t*t + b
    end,    
    easeOutCubic= function(t, b, c, d)
        local t = t/d
        return c*((t-1)*t*t + 1) + b
    end,    
    easeInOutCubic= function(t, b, c, d)
        local t = t/d*2
        if t < 1 then return c/2*t*t*t + b end
        t=t-2
        return c/2*(t*t*t + 2) + b
    end,    
    easeOutInCubic= function(t, b, c, d)
        if t < d/2 then return Tweener._easingFunctions.easeOutCubic(t*2, b, c/2, d) end
        return Tweener._easingFunctions.easeInCubic((t*2)-d, b+c/2, c/2, d)
    end,    
    easeInQuart= function(t, b, c, d)
        local t = t/d
        return c*t*t*t*t + b
    end,    
    easeOutQuart= function(t, b, c, d)
        local t = t/d
        return -c *((t-1)*t*t*t - 1) + b
    end,    
    easeInOutQuart= function(t, b, c, d)
        local t = t/d*2
        if t < 1 then return c/2*t*t*t*t + b end
        
        t = t-2
        return -c/2 *(t*t*t*t - 2) + b
    end,    
    easeOutInQuart= function(t, b, c, d)
        if t < d/2 then return Tweener._easingFunctions.easeOutQuart(t*2, b, c/2, d) end
        return Tweener._easingFunctions.easeInQuart((t*2)-d, b+c/2, c/2, d)
    end,    
    easeInQuint= function(t, b, c, d)
        local t = t/d
        return c*t*t*t*t*t + b
    end,    
    easeOutQuint= function(t, b, c, d)
        t = t/d
        return c*((t-1)*t*t*t*t + 1) + b
    end,    
    easeInOutQuint= function(t, b, c, d)
        t = t/d*2
        if t < 1 then return c/2*t*t*t*t*t + b end
        
        t=t-2
        return c/2*(t*t*t*t*t + 2) + b
    end,    
    easeOutInQuint= function(t, b, c, d)
        if t < d/2 then return Tweener._easingFunctions.easeOutQuint(t*2, b, c/2, d) end
        return Tweener._easingFunctions.easeInQuint((t*2)-d, b+c/2, c/2, d)
    end,    
    easeInSine= function(t, b, c, d)
        return -c * math.cos(t/d *(math.pi/2)) + c + b
    end,    
    easeOutSine= function(t, b, c, d)
        return c * math.sin(t/d *(math.pi/2)) + b
    end,    
    easeInOutSine= function(t, b, c, d)
        return -c/2 *(math.cos(math.pi*t/d) - 1) + b
    end,    
    easeOutInSine= function(t, b, c, d)
        if t < d/2 then return Tweener._easingFunctions.easeOutSine(t*2, b, c/2, d) end
        return Tweener._easingFunctions.easeInSine((t*2)-d, b+c/2, c/2, d)
    end,    
    easeInExpo= function(t, b, c, d)
        return (t==0) and b or c * math.pow(2, 10 *(t/d - 1)) + b - c * 0.001
    end,    
    easeOutExpo= function(t, b, c, d)
        return (t==d) and b+c or c * 1.001 *(-math.pow(2, -10 * t/d) + 1) + b
    end,    
    easeInOutExpo= function(t, b, c, d)
        if t==0  then return b end
        if t==d  then return b+c end
        
        t=t/d*2
        if t< 1 then return c/2 * math.pow(2, 10 *(t - 1)) + b - c * 0.0005 end
        
        t=t-1
        return c/2 * 1.0005 *(-math.pow(2, -10 * t) + 2) + b
    end,    
    easeOutInExpo= function(t, b, c, d)
        if(t < d/2) then return Tweener._easingFunctions.easeOutExpo(t*2, b, c/2, d) end
        return Tweener._easingFunctions.easeInExpo((t*2)-d, b+c/2, c/2, d)
    end,    
    easeInCirc= function(t, b, c, d)
        t = t/d
        return -c *(math.sqrt(1 -t*t) - 1) + b
    end,    
    easeOutCirc= function(t, b, c, d)
        t=t/d
        return c * math.sqrt(1 -(t-1)*t) + b
    end,    
    easeInOutCirc= function(t, b, c, d)
        t=t/d*2
        if t<1 then return -c/2 *(math.sqrt(1 - t*t) - 1) + b end
        t=t-2
        return c/2 *(math.sqrt(1 -t*t) + 1) + b
    end,    
    easeOutInCirc= function(t, b, c, d)
        if t < d/2  then return Tweener._easingFunctions.easeOutCirc(t*2, b, c/2, d) end
        return Tweener._easingFunctions.easeInCirc((t*2)-d, b+c/2, c/2, d)
    end,    
    easeInElastic= function(t, b, c, d, a, p)
        local s
        if t==0 then return b end
        t = t/d
        if t==1 then return b+c end  
        if not p then p=d*.3 end
        
        if  not a or a < math.abs(c)  then 
            a=c s=p/4 
        else 
            s = p/(2*math.pi) * math.asin(c/a)
        end
        t = t-1
        return -(a*math.pow(2,10*t) * math.sin((t*d-s)*(2*math.pi)/p )) + b
    end,    
    easeOutElastic= function(t, b, c, d, a, p)
        local s
        if t==0  then return b end  
        
        t = t/d 
        if (t==1) then return b+c end  
        if not p then p=d*.3 end
        if (not a or a < math.abs(c)) then 
            a=c
            s=p/4
        else 
            s = p/(2*math.pi) * math.asin(c/a)
        end
        return(a*math.pow(2,-10*t) * math.sin((t*d-s)*(2*math.pi)/p ) + c + b)
    end,    
    easeInOutElastic= function(t, b, c, d, a, p)
        local s
        if t==0  then return b  end 
        
        t = t/d*2
        if t==2 then return b+c  end
        if not p then p=d*(.3*1.5) end
        if(not a or a < math.abs(c)) then a=c s=p/4 
        else s = p/(2*math.pi) * math.asin(c/a) end
        
        if t<1 then 
            t = t-1
            return -.5*(a*math.pow(2,10*t) * math.sin((t*d-s)*(2*math.pi)/p )) + b 
        end
        
        t = t-1
        return a*math.pow(2,-10*t) * math.sin((t*d-s)*(2*math.pi)/p )*.5 + c + b
    end,    
    easeOutInElastic= function(t, b, c, d, a, p)
        if t< d/2 then return Tweener._easingFunctions.easeOutElastic(t*2, b, c/2, d, a, p) end
        
        return Tweener._easingFunctions.easeInElastic((t*2)-d, b+c/2, c/2, d, a, p)
    end,    
    easeInBack= function(t, b, c, d, s)
        if not s then s = 1.70158 end
        t = t/d
        return c*t*t*((s+1)*t - s) + b
    end,    
    easeOutBack= function(t, b, c, d, s)
        if not s then s = 1.70158 end
        
        t = t/d
        return c*((t-1)*t*((s+1)*t + s) + 1) + b
    end,    
    easeInOutBack= function(t, b, c, d, s)
       if not s then s = 1.70158 end
        
        t = t/d*2
        s = s*1.525
        if t<1 then 
            return c/2*(t*t*((s+1)*t - s)) + b 
        end
        
        t = t-2
        return c/2*(t*t*((s+1)*t + s) + 2) + b
    end,    
    easeOutInBack= function(t, b, c, d, s)
        if t<d/2 then return Tweener._easingFunctions.easeOutBack(t*2, b, c/2, d, s) end
        return Tweener._easingFunctions.easeInBack((t*2)-d, b+c/2, c/2, d, s)
    end,    
    easeInBounce= function(t, b, c, d)
        return c - Tweener._easingFunctions.easeOutBounce(d-t, 0, c, d) + b
    end,    
    easeOutBounce= function(t, b, c, d)
        t = t/d
        if(t <(1/2.75)) then
            return c*(7.5625*t*t) + b
        elseif (t <(2/2.75)) then
            t = t-1.5/2.75
            return c*(7.5625*t*t + .75) + b
        elseif(t <(2.5/2.75)) then
            t = t-2.25/2.75
            return c*(7.5625*t*t + .9375) + b
        else
            t = t-2.625/2.75
            return c*(7.5625*t*t + .984375) + b
        end
    end,    
    easeInOutBounce= function(t, b, c, d)
        if t < d/2 then return Tweener._easingFunctions.easeInBounce(t*2, 0, c, d) * .5 + b end
        return Tweener._easingFunctions.easeOutBounce(t*2-d, 0, c, d) * .5 + c*.5 + b
    end,    
    easeOutInBounce= function(t, b, c, d)
        if t < d/2 then 
            return Tweener._easingFunctions.easeOutBounce(t*2, b, c/2, d)
        end
        return Tweener._easingFunctions.easeInBounce((t*2)-d, b+c/2, c/2, d)
    end
}
Tweener._easingFunctions.linear = Tweener._easingFunctions.easeNone



return Tweener